use std::{
    fs::{self, create_dir_all, File},
    io::Result,
    time::Instant,
};

use crc32fast::Hasher;
use crc64fast::Digest;
use memmap2::MmapOptions;
use oracle::OracleComparer;
use tdc::{Action, GlobalConfig, RuleMap};
mod oracle;

fn _compute_crc_mmap(file_path: &str) -> Result<u32> {
    let file = File::open(file_path)?;
    let mmap = unsafe { MmapOptions::new().map(&file)? }; // 内存映射优化‌:ml-citation{ref="4" data="citationList"}

    let mut hasher = Hasher::new();
    let chunk_size = 1024 * 1024; // 1MB分块‌:ml-citation{ref="5" data="citationList"}
    let total = mmap.len();
    let mut processed = 0;

    // 进度监控（每处理1GB输出进度）
    let start_time = Instant::now();

    while processed < total {
        let end = (processed + chunk_size).min(total);
        let chunk = &mmap[processed..end];

        hasher.update(chunk); // 分块更新哈希‌:ml-citation{ref="2" data="citationList"}
        processed = end;

        if processed % (1024 * 1024 * 1024) == 0 {
            // 每GB输出进度
            let speed = processed as f64 / start_time.elapsed().as_secs_f64() / 1e6;
            println!(
                "Processed: {:.2}GB | Speed: {:.2}MB/s",
                processed as f64 / 1e9,
                speed
            );
        }
    }

    Ok(hasher.finalize())
}

fn _compute_crc64_mmap(file_path: &str) -> Result<u64> {
    let file = File::open(file_path)?;
    let mmap = unsafe { MmapOptions::new().map(&file)? }; // 内存映射优化‌:ml-citation{ref="4" data="citationList"}

    let mut c = Digest::new();
    let chunk_size = 1024 * 1024; // 1MB分块‌:ml-citation{ref="5" data="citationList"}
    let total = mmap.len();
    let mut processed = 0;

    // 进度监控（每处理1GB输出进度）
    let start_time = Instant::now();

    while processed < total {
        let end = (processed + chunk_size).min(total);
        let chunk = &mmap[processed..end];

        c.write(chunk); // 分块更新哈希‌:ml-citation{ref="2" data="citationList"}
        processed = end;

        if processed % (1024 * 1024 * 1024) == 0 {
            // 每GB输出进度
            let speed = processed as f64 / start_time.elapsed().as_secs_f64() / 1e6;
            println!(
                "Processed: {:.2}GB | Speed: {:.2}MB/s",
                processed as f64 / 1e9,
                speed
            );
        }
    }

    Ok(c.sum64())
}

#[tokio::main]
async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
    // println!("{:?}", ComparisonRuleMap::from("LBK1=>[!TAB1,Ta]:LBK2=>[!TAB2,Tb]"));
    // // println!("{:?}", ComparisonRuleMap::from("LBK1=>[!TAB1,Ta] LBK2=>[!TAB2,Tb]"));
    // println!("{:?}", ComparisonRuleMap::from("LBK1.TEST1=>[!ID]:LBK2.TEST2=>[!ID]"));
    // println!("{:?}", ComparisonRuleMap::from("ACTOR=>[!COL1,!COL2,C2,C3,C4,?ID>100 AND \"AGE\"> 18]:ACTOR=>[!COL1,!COL2,C2,C3,C4,?ID>100 AND \"AGE\"> 18]"));

    // exit(-1);

    // match option_env!("TDC_LOG4RS") {
    //     Some(file) => log4rs::init_file(file, Default::default()).unwrap(),
    //     None => {
    //         println!("WARN: The environment variable TDC_LOG4RS is not set, and no logs will be output");
    //         exit(-1)
    //     }
    // }
    log4rs::init_file("./log4rs.toml", Default::default()).unwrap();

    // exit(0);

    // let now = Local::now();
    // match compute_crc_mmap("C:\\Users\\BK-liao\\Documents\\engineDsg.tar.gz") {
    //     Ok(crc) => {
    //         println!("{:08x}", crc)
    //     }
    //     Err(e) => eprintln!("Error: {:?}", e),
    // }

    // let now = Local::now() - now;

    // println!("cost: {}.{}secs", now.num_seconds(), now.num_milliseconds());

    // let now = Local::now();
    // match compute_crc64_mmap("C:\\Users\\BK-liao\\Documents\\engineDsg.tar.gz") {
    //     Ok(crc) => {
    //         println!("{:08x}", crc)
    //     }
    //     Err(e) => eprintln!("Error: {:?}", e),
    // }

    // let now = Local::now() - now;

    // println!("cost: {}.{}secs", now.num_seconds(), now.num_milliseconds());

    let mut compare_config = tdc::CompareConfig::new();
    compare_config.add_schema("LBK1".to_owned());

    let content = fs::read_to_string("config.toml").unwrap();
    let mut global_config: GlobalConfig = toml::from_str(&content).unwrap();
    log::debug!("{:?}", global_config);

    match global_config.action {
        Action::Unknown { value } => {
            panic!("[{}] Illegal parameter: action = {value}", "config.toml");
        }
        _ => {}
    }
    if !global_config.tmpdir.exists() {
        if let Err(e) = create_dir_all(&global_config.tmpdir) {
            log::error!("mkdir tmpdir `{:?}` failed, {e}", &global_config.tmpdir);
        }
    }

    if let Some(rule_file) = global_config.clone().rule_file {
        if !rule_file.exists() {
            panic!(
                "[{}] Illegal parameter: rule_file = {:?}: No Such File",
                "config.toml",
                rule_file.to_string_lossy()
            );
        } else {
            // 解析文件
            match fs::read_to_string(&rule_file) {
                Ok(rule_content) => {
                    let mut rules = Vec::new();
                    for line in rule_content.lines() {
                        rules.push(RuleMap::from(line));
                    }
                    global_config.rule = Some(rules);
                }
                Err(e) => panic!("File {:?} open failed, {e}", rule_file.to_string_lossy()),
            }
        }
    }

    let mut rule_count = 0;
    if let Some(rm) = global_config.clone().rule {
        rule_count = rm.len();
    }
    if rule_count == 0 || global_config.clone().rule.is_none() {
        panic!(
            "[{}] Illegal parameter: rule = \"\": No Such File",
            "config.toml",
        );
    }

    // let db_config = DatabaseConfig {
    //     dbtype: "Oracle".to_owned(),
    //     username: "lbk".to_owned(),
    //     password: "Oracle".to_owned(),
    //     host: "192.168.6.250".to_owned(),
    //     port: 11521,
    //     database: "orcl".to_owned(),
    // };
    match global_config.db1.dbtype {
        tdc::DatabaseType::Oracle => {
            let comparer = OracleComparer::new(global_config.clone(), global_config.clone().db1);
            comparer.compare(oracle::Typ::SRC).await;
        }
        _ => {}
    }

    match global_config.db2.dbtype {
        tdc::DatabaseType::Oracle => {
            let comparer = OracleComparer::new(global_config.clone(), global_config.clone().db2);
            comparer.compare(oracle::Typ::TGT).await;
        }
        _ => {}
    }

    // let res = compare_lines_parallel(Path::new("C:\\Users\\BK-liao\\Desktop\\1.txt"), Path::new("C:\\Users\\BK-liao\\Desktop\\2.txt"));
    // log::debug!("{:?}", res);

    Ok(())
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;

    macro_rules! is_regex {
        ($s:expr) => {
            ($s.trim().starts_with("/") || $s.trim().starts_with("!/")) && $s.trim().ends_with("/")
        };
    }

    macro_rules! not_contains {
        ($s:expr) => {
            $s.trim().starts_with("!")
        };
    }

    #[test]
    fn test2() {
        let s = "/LBK[0-9]{1,10}/, TEST, ! /test/";
        // let s = "/LBK[0-9]{1,}/, TEST";
        let mut values = vec![];

        let mut strings = String::new();
        let mut open_regex = false;
        for ch in s.chars() {
            match ch {
                ',' => {
                    // next
                    if open_regex {
                        strings.push(ch);
                    } else if strings.trim().len() > 0 {
                        values.push(strings.clone().trim().to_owned());
                        strings.clear();
                    }
                }
                '/' => {
                    // Open Or Close Regex
                    strings.push(ch);
                    if !open_regex {
                        open_regex = true;
                    } else {
                        // Close Regex
                        if strings.trim().len() > 0 {
                            values.push(strings.clone().trim().to_owned());
                        }
                        strings.clear();
                        open_regex = false;
                    }
                }
                _ => {
                    strings.push(ch);
                }
            }
        }
        if strings.trim().len() > 0 {
            values.push(strings.clone().trim().to_owned());
        }

        println!("{:?}", values);
    }

    fn parse_str<T: AsRef<str>>(s: &T) -> Vec<String>
    where
        T: ?Sized,
    {
        let mut values = vec![];
        let mut strings = String::new();
        let mut open_regex = false;
        for ch in s.as_ref().chars() {
            match ch {
                ',' => {
                    // next
                    if open_regex {
                        strings.push(ch);
                    } else if strings.trim().len() > 0 {
                        values.push(strings.clone().trim().to_owned());
                        strings.clear();
                    }
                }
                '/' => {
                    // Open Or Close Regex
                    strings.push(ch);
                    if !open_regex {
                        open_regex = true;
                    } else {
                        // Close Regex
                        if strings.trim().len() > 0 {
                            values.push(strings.clone().trim().to_owned());
                        }
                        strings.clear();
                        open_regex = false;
                    }
                }
                _ => {
                    strings.push(ch);
                }
            }
        }
        if strings.trim().len() > 0 {
            values.push(strings.clone().trim().to_owned());
        }
        values
    }

    #[derive(Debug, Clone)]
    struct RuleValue {
        pub re: bool,
        pub value: String,
        pub not: bool,
    }

    impl RuleValue {
        pub fn new(value: String, re: bool, not: bool) -> Self {
            RuleValue { value, re, not }
        }
    }

    #[derive(Eq, Hash, PartialEq, Debug, Clone)]
    enum KeyType {
        SCHEMA,
        TABLE,
        COLUMN,
        CONDITION,
    }

    #[test]
    fn parse_rule() {
        // let hay = "LBK1=>[!TAB2]:LBK2=>[!TAB2]";
        // let hay = "LBK1=>[!TAB2]:LBK2=>[!TAB2]";
        let hay = "LBK1.TTTT";
        // let hay = "/LBK1{1,}/,/LBK2{1,}/./TTT{1,}/,/AAA{1,}/=>C1, C2, (a = 100 and b >= 10)";
        // let hay = "/LBK1{1,}/,/LBK2{1,}/./TTT{1,}/,/AAA{1,}/=>C1, C2 (a = 100 and b >= 10)";
        // let hay = "LBK1=>C1, C2";
        // let hay = "LBK1";
        // let hay = "LBK1=>(a < 100 and b >= 10)";
        // let hay = "LBK1./*/=>[(a < 100 and b >= 10)]";
        // let hay = "/!TEST/=>[(a < 100 and b >= 10)]";
        // let hay = "/!TEST/.TT=>[(a = 100 and b >= 10)]";
        // let hay = "/!TEST/.TT=>[!ID]";
        // let hay = "/!TEST/.TT";
        // let hay = "/!TEST/.TT=>[!ID,!C100 (a = 100 and b >= 10)]";
        // let hay = "/!TEST/.!/TTT_*/=>[!ID,!C100 (a = 100 and b >= 10)]";
        // let hay = "/!TEST/.[!/TTT_*/,!/TEST*/,!EXT_EMPLOYEES]=>[!ID,!C100 (a = 100 and b >= 10)]";
        // let hay = "TEST.[!/TTT_*/,!/TEST*/,!EXT_EMPLOYEES]=>[!ID,!C100 (a = 100 and b >= 10)]";
        // let hay = "[/^((([1-9][0-9]?)|(1[0-9]{2})|(2[0-4][0-9])|(25[0-5]))\\.aaa+$/, TEST, !/TTT*/, !TD,!T100].[!/TTT_*/,!/TEST*/,!EXT_EMPLOYEES]=>[!ID,!C100 (a = 100 and b >= 10)]";
        let mut values = Vec::new();
        let mut map = HashMap::new();
        let mut array_open = false;
        let mut regex_open = false;
        let mut cond_open = false;
        let mut strings = String::new();
        let mut key: KeyType = KeyType::SCHEMA;
        let mut rules = Vec::new();

        for ch in hay.trim().chars() {
            match ch {
                '/' => {
                    if regex_open {
                        if strings.len() > 0 {
                            let value = strings.clone().trim().to_owned();
                            let (value, not) = if value.starts_with("!") {
                                (value.trim_start_matches("!"), true)
                            } else {
                                (value.as_str(), false)
                            };
                            // println!("{:?}", ConfigEntity::new(value.to_owned(), regex_open, not));
                            values.push(RuleValue::new(value.to_owned(), regex_open, not));
                            strings.clear();
                        }
                    }
                    regex_open = !regex_open;
                }
                '[' => {
                    if regex_open {
                        strings.push(ch);
                        continue;
                    }
                    array_open = true;
                }
                // ']' => {
                //     if regex_open {
                //         strings.push(ch);
                //     } else {
                //         if strings.len() > 0 {
                //             let value = strings.clone().trim().to_owned();
                //             let (value, not) = if value.starts_with("!") {
                //                 (value.trim_start_matches("!"), true)
                //             } else {
                //                 (value.as_str(), false)
                //             };
                //             // println!("{:?}", ConfigEntity::new(value.to_owned(), regex_open, not));
                //             values.push(ConfigEntity::new(value.to_owned(), regex_open, not));
                //             strings.clear();
                //         }

                //     }
                // }
                ',' | '.' | ']' | '(' => {
                    if regex_open {
                        strings.push(ch);
                        continue;
                    }

                    if strings.len() > 0 {
                        let value = strings.clone().trim().to_owned();
                        let (value, not) = if value.starts_with("!") {
                            (value.trim_start_matches("!"), true)
                        } else {
                            (value.as_str(), false)
                        };
                        values.push(RuleValue::new(value.to_owned(), regex_open, not));
                        strings.clear();
                    }

                    match ch {
                        '.' => {
                            if values.len() > 0 {
                                // Schema 单个正则
                                // if values.len() > 1 {
                                //     panic!("invalid format, lost square brackets on part {:?}", key);
                                // }
                                map.insert(key.clone(), values.clone());
                                values.clear();
                            }
                            if key == KeyType::SCHEMA {
                                key = KeyType::TABLE;
                            }
                        }
                        ']' => {
                            if array_open {
                                map.insert(key.clone(), values.clone());
                                values.clear();
                                array_open = false;
                            }
                        }
                        '(' => {
                            if values.len() > 0 {
                                map.insert(key.clone(), values.clone());
                                values.clear();
                            }
                            if key == KeyType::COLUMN {
                                key = KeyType::CONDITION;
                                cond_open = true;
                            }
                        }
                        _ => {}
                    }
                }
                // '.' => {
                //     if regex_open {
                //         strings.push(ch);
                //     } else {
                //         if strings.len() > 0 {
                //             //
                //             let value = strings.clone().trim().to_owned();
                //             let (value, not) = if value.starts_with("!") {
                //                 (value.trim_start_matches("!"), true)
                //             } else {
                //                 (value.as_str(), false)
                //             };
                //             values.push(ConfigEntity::new(value.to_owned(), regex_open, not));
                //             strings.clear();
                //         }

                //     }
                // }
                // '=' => {
                //     strings.push(ch);
                // }
                '>' => {
                    if strings == "=" {
                        if values.len() > 0 {
                            // Table 单个正则
                            // if values.len() > 1 {
                            //     panic!("invalid format, lost square brackets on part {:?}", key);
                            // }
                            map.insert(key.clone(), values.clone());
                            values.clear();
                        }
                        strings.clear();
                        key = KeyType::COLUMN;
                    } else if strings.ends_with("=") {
                        // Table 为全匹配
                        // if values.len() > 1 {
                        //     panic!("invalid format, lost square brackets on part {:?}", key);
                        // }
                        let value = strings.trim_end_matches("=").to_owned();
                        let (value, not) = if value.starts_with("!") {
                            (value.trim_start_matches("!"), true)
                        } else {
                            (value.as_str(), false)
                        };
                        values.push(RuleValue::new(value.to_owned(), regex_open, not));
                        map.insert(key.clone(), values.clone());
                        values.clear();
                        strings.clear();
                        key = KeyType::COLUMN;
                    } else {
                        strings.push(ch);
                    }
                }
                // '(' => {
                //     if regex_open {
                //         strings.push(ch);
                //     } else {
                //         // 结束上一个
                //         if strings.len() > 0 {
                //             let value = strings.clone().trim().to_owned();
                //             let (value, not) = if value.starts_with("!") {
                //                 (value.trim_start_matches("!"), true)
                //             } else {
                //                 (value.as_str(), false)
                //             };
                //             // println!("{:?}", ConfigEntity::new(value.to_owned(), regex_open, not));
                //             values.push(ConfigEntity::new(value.to_owned(), regex_open, not));
                //             strings.clear();

                //             map.insert(key.clone(), values.clone());
                //             values.clear();

                //         }

                //         match key {
                //             KeyType::COLUMN => {
                //                 cond_open = true;
                //                 key = KeyType::CONDITION;
                //             }
                //             _ => {}
                //         }
                //     }
                // }
                ')' => {
                    if regex_open {
                        strings.push(ch);
                        continue;
                    }

                    if cond_open {
                        // println!("condition: {:?}", strings.clone().trim());
                        values.push(RuleValue::new(
                            strings.clone().trim().to_owned(),
                            false,
                            false,
                        ));
                        strings.clear();
                        cond_open = false;
                    }
                }
                ':' => {
                    if regex_open {
                        strings.push(ch);
                        continue;
                    }
                    // 新的一个映射开始
                    println!("left-map: {:?}", map);
                    rules.push(map.clone());
                    map.clear();
                    key = KeyType::SCHEMA;
                }
                _ => {
                    strings.push(ch);
                }
            }
        }

        match key {
            KeyType::SCHEMA | KeyType::TABLE | KeyType::COLUMN => {
                if strings.len() > 0 {
                    let value = strings.clone().trim().to_owned();
                    let (value, not) = if value.starts_with("!") {
                        (value.trim_start_matches("!"), true)
                    } else {
                        (value.as_str(), false)
                    };
                    values.push(RuleValue::new(value.to_owned(), regex_open, not));
                    map.insert(key.clone(), values.clone());
                    values.clear();
                    strings.clear();
                }
            }
            KeyType::CONDITION => {
                if values.len() > 0 {
                    // Table 单个正则
                    // if values.len() > 1 {
                    //     panic!("invalid format, lost square brackets on part {:?}", key);
                    // }
                    map.insert(key.clone(), values.clone());
                    values.clear();
                }
            }
        }

        rules.push(map.clone());
        if rules.len() == 1 {
            rules.push(map.clone());
        }
        println!("map: {:?}", rules);
    }

    #[test]
    fn test_schema_map() {
        // schema
        //       : table
        //               : column
        //       : condition

        let hay = "[/^((([1-9][0-9]?)|(1[0-9]{2})|(2[0-4][0-9])|(25[0-5]))\\.aaa+$/, TEST, !/TTT*/, !ID,!C100].[!/TTT_*/,!/TEST*/,!EXT_EMPLOYEES]=>[!ID,!C100]";
        // let hay = "[/LBK*/, TEST].[!/TTT_*/,!/TEST*/,!EXT_EMPLOYEES]";
        // let hay = "[/LBK*/, TEST]=>[!ID]";
        // let hay = "[/LBK*/, TEST]=>[!ID]";
        // let hay = "TEST=>[!ID]";

        // println!("{:?}", hay);
        // let mut open_arrays = None;
        // let mut strings = String::new();
        // for ch in hay.trim().chars() {
        //     match ch {
        //         '[' => {
        //             // Open Arrays
        //             // open_arrays = Some(Vec::new());
        //         }
        //         ']' => {
        //             // Closed Arrays
        //             // open_arrays = None;
        //         }
        //         ',' => {
        //             // next
        //         }
        //         '/' => {
        //             // Open Or Close Regex
        //         }
        //         '.' => {
        //             // When Schema Closed.
        //         }
        //         '!' => {
        //             // Exclude, For Tables, Columns
        //         }
        //         '=' => {

        //         }
        //         '>' => {
        //             //
        //         },
        //         '?' => {
        //             // Condition
        //         }
        //         _ => {
        //             strings.push(ch);
        //         }
        //     }
        // }

        // let ss: Vec<&str> = hay.split_terminator('.').collect();
        // for str in ss {
        //     println!("str: {str}")
        // }

        fn handle_schema(s: &str) {
            println!("schema: {s}");
            let mut in_values = vec![];
            let mut not_in_values = vec![];
            let mut regexp_likes = vec![];
            let mut cond_regexp_likes = vec![];
            let mut not_regexp_likes = vec![];
            let mut cond_not_regexp_likes = vec![];

            let s = s.trim();
            if s.starts_with("[") && s.ends_with("]") {
                // 多个
                let s = s.trim_start_matches("[").trim_end_matches("]").trim();
                for name in parse_str(s) {
                    if !is_regex!(name) {
                        if not_contains!(name) {
                            not_in_values.push(name.trim_start_matches("!").trim().to_owned());
                        } else {
                            in_values.push(name.trim().to_owned());
                        }
                        continue;
                    }

                    if not_contains!(name) {
                        let value = name.trim_start_matches("!/").trim_end_matches("/").trim();
                        not_regexp_likes.push(value.to_owned());
                        cond_not_regexp_likes
                            .push(format!("NOT REGEXP_LIKE (USERNAME, '{}')", value));
                    } else {
                        let value = name.trim_start_matches("/").trim_end_matches("/").trim();
                        regexp_likes.push(value.to_owned());
                        cond_regexp_likes.push(format!("REGEXP_LIKE (USERNAME, '{}')", value));
                    }
                }
            } else {
                // 只有一个，且全匹配
                in_values.push(s.to_owned());
            }

            println!("in_values:{:?}", in_values);
            println!("regexp_likes:{:?}", regexp_likes);
            println!("not_in_values:{:?}", not_in_values);
            println!("not_regexp_likes:{:?}", not_regexp_likes);

            // let s = if in_values.len() > 0 {
            //     let mut strings = String::from(format!("USERNAME IN ({})", in_values.join(",")));
            //     if cond_regexp_likes.len() > 0 {
            //         strings.push_str(&format!(" AND ({})", cond_regexp_likes.join(" OR ")));
            //     }
            //     strings
            // } else if not_in_values.len() > 0 {
            //     let mut strings =
            //         String::from(format!("USERNAME NOT IN ({})", not_in_values.join(",")));
            //     if cond_not_regexp_likes.len() > 0 {
            //         strings.push_str(&format!(" OR ({})", cond_not_regexp_likes.join(" AND ")));
            //     }
            //     strings
            // } else {
            //     String::new()
            // };

            // println!("select username from dba_users where {}", s);
        }

        // ALL_TABLE
        // NO_CONDITION
        match hay.split_once('.') {
            Some((s, tc)) => {
                //
                handle_schema(s);

                match tc.split_once("=>") {
                    Some((t, cond)) => {
                        println!("table: {t}");
                        println!("cond: {cond}");
                    }
                    None => {
                        // NO_CONDITION
                        let t = tc;
                        println!("table: {t}");
                    }
                }
            }
            None => {
                // ALL_TABLE
                let sc = hay;
                match sc.split_once("=>") {
                    Some((s, cond)) => {
                        println!("schema: {s}");
                        println!("cond: {cond}");
                    }
                    None => {
                        // NO_CONDITION
                        let s = sc;
                        handle_schema(s);
                        // println!("schema: {s}");
                        // for s0 in s.split(",") {
                        //     let s0 = s0.trim();
                        //     println!("REGEXP_LIKE (USERNAME, :{s0})");
                        // }
                    }
                }
            }
        }

        // let re = Regex::new(r"\[.*\]").unwrap();

        // let mut results = vec![];
        // for (_, [path, lineno, line]) in re.captures_iter(hay).map(|c| c.extract()) {
        //     results.push((path, lineno.parse::<u64>().unwrap(), line));
        // }

        // println!("{:?}", results);
    }
}
